import { exec } from 'child_process';
import os from 'os';
import path from 'path';

import { expect, test } from '@jest/globals';
import { isWindows } from '@ohos/hvigor';
import { initNodePath } from '@ohos/hvigor/src/cli/wrapper/prepare-node-path';
import * as execa from 'execa';
import fse from 'fs-extra';

import { commonConfig } from '../common-config';
import { commonUtil } from '../common-util';
import { TaskStatus } from '../project-builder/models/constants';
import { FilterOption, TaskTags } from '../project-builder/models/model-options';
import { reportBuilder } from '../report-builder/report-builder';
import { taskReport } from '../report-builder/report-models';
import { TestCase } from './test-case';
import { testCaseFactory } from './test-case-init';

/**
 * 测试任务中心类
 */
class TestTaskCenter {
  /**
   * 针对偶现问题场景执行重复测试验证的函数，单次循环次数不得大于100
   *
   * @param {string} caseName
   * @param {number} times
   * @param {boolean} concurrent
   * @return {Promise<void>}
   */
  public async loopTestTask(caseName: string, times: number, concurrent = true): Promise<void> {
    const testCases = testCaseFactory.findTestCases({
      caseName,
    });
    if (testCases.length !== 1) {
      expect('找到了一个对应的测试用例').toEqual('未找到对应测试用例');
    }
    const testCase = testCases[0];

    const { reportRootPath, tempPath } = this.preExecuteTasks(testCases);

    /**
     * 定义全局钩子，完成测试报告输出工作
     */
    afterAll(this.getAftFn(reportRootPath));

    /**
     * 遍历用例，并行执行
     */
    if (concurrent) {
      for (let i = 0; i < times; i++) {
        test.concurrent(testCase.caseName, async () => testCase.run(tempPath));
      }
    } else {
      for (let i = 0; i < times; i++) {
        test(testCase.caseName, async () => testCase.run(tempPath));
      }
    }
  }

  /**
   * 执行测试用例
   *
   * @param {Partial<TaskTags>} filter
   */
  public async runTask(filter?: Partial<TaskTags> | FilterOption<TaskTags>): Promise<Array<{ caseName: string; error: Error }>> {
    // 过滤出需要执行的用例
    const testCases: TestCase[] = testCaseFactory.findTestCases(filter);
    const failedCases: Array<{ caseName: string; error: Error }> = [];

    const { reportRootPath, tempPath } = this.preExecuteTasks(testCases);

    /**
     * 定义全局钩子，完成测试报告输出工作
     */
    afterAll(this.getAftFn(reportRootPath));

    /**
     * 遍历用例，并行执行
     * 0402更新，将单个用例的error catch住，然后写进日志里统一处理
     */
    const testPromises = testCases.map( testCase => {
      return new Promise(resolve => {
        test.concurrent(testCase.caseName, async () => {
          try {
            await testCase.run(tempPath);
          } catch (error) {
            failedCases?.push({caseName: testCase.caseName, error: error as Error});
          }
          resolve(true);
        });
      });
    });
    await Promise.all(testPromises);

    return failedCases;
  }

  private preExecuteTasks(testCases: TestCase[]) {
    // 报告输出根路径
    const reportRootPath = path.join(commonConfig.root, 'e2e-report', `Report_${commonConfig.createDataStr()}`);

    // 复制jQuery插件
    fse.copySync(path.join(__dirname, '../', 'report-builder', 'report-template', 'jquery-1.9.1.js'), path.join(reportRootPath, 'js', 'jquery-1.9.1.js'));

    taskReport.rootPath = reportRootPath;
    taskReport.reportPath = path.join(reportRootPath, 'report.html');
    taskReport.startTime = new Date(Date.now());
    taskReport.allTask = testCases.length;

    // 初始化 NodePath
    initNodePath(commonConfig.workplace);

    // 初始化ohpm配置
    this.initOhpmConfig();

    // 工程文件输出路径
    const tempPath = path.join(commonConfig.root, 'e2e-project', `Project_${commonConfig.createDataStr()}`);
    return { reportRootPath, tempPath };
  }

  private getAftFn(reportRootPath: string): () => Promise<void> {
    return async () => {
      // 全部任务完成时间
      taskReport.endTime = new Date(Date.now());

      // 汇总任务执行状态
      for (const record of Object.values(taskReport.records)) {
        if (record.taskStatus === TaskStatus.Pass) {
          taskReport.passTask++;
        } else if (record.taskStatus === TaskStatus.Fail) {
          taskReport.failedTask++;
        } else if (record.taskStatus === TaskStatus.NotExecuted) {
          taskReport.noRunTask++;
        }
      }

      // 清除两天前的记录文件
      const rootPath = path.resolve(reportRootPath, '../', '../');
      await this.deleteOldSubdirectories(path.resolve(rootPath, 'e2e-report'), 2);
      await this.deleteOldSubdirectories(path.resolve(rootPath, 'e2e-project'), 2);

      // 打开报告文件夹
      if (isWindows()) {
        // 输出报告
        await reportBuilder.buildTaskReport(taskReport);
        exec(`explorer ${taskReport.rootPath!}`);
      } else {
        // 执行结果输出到文件
        const resultFile = path.join(__dirname, '../', '../', '../', '../', '../', '../', 'result');
        // eslint-disable-next-line max-len
        await fse.appendFile(resultFile, `e2e-result|${taskReport.allTask}|${taskReport.passTask}|${taskReport.allTask - taskReport.passTask}${os.EOL}`);
        const logFile = path.join(__dirname, '../', '../', '../', '../', '../', '../', 'log');
        for (const record of Object.values(taskReport.records)) {
          if (record.taskStatus !== TaskStatus.Pass) {
            await fse.appendFile(logFile, `++++++++++${record.caseName}++++++++++${os.EOL}`);
            await fse.appendFile(logFile, `++++++++++${record.caseName}++++++++++${os.EOL}`);
            await fse.appendFile(logFile, `++++++++++${record.caseName}++++++++++${os.EOL}`);
            for (const [step, result] of record.results) {
              await fse.appendFile(logFile, `=====================${step} console log=====================${os.EOL}`);
              await fse.appendFile(logFile, commonUtil.removeANSI(result.stdout));
              await fse.appendFile(logFile, `${os.EOL}${os.EOL}${os.EOL}`);
              await fse.appendFile(logFile, `=====================${step} error log=====================${os.EOL}`);
              await fse.appendFile(logFile, commonUtil.removeANSI(result.stderr));
              await fse.appendFile(logFile, `${os.EOL}${os.EOL}${os.EOL}`);
            }
            await fse.appendFile(logFile, os.EOL);
            await fse.appendFile(logFile, os.EOL);
            await fse.appendFile(logFile, os.EOL);
          }
        }
      }
    };
  }

  private initOhpmConfig() {
    const configList: string[] = [
      // eslint-disable-next-line max-len
      'ohpm config set registry https://7.190.6.197:8088/repos/ohpm,http://10.243.63.82:8088/repos/ohpm,https://cmc.centralrepo.rnd.huawei.com/artifactory/api/npm/product_npm/,https://cmc.centralrepo.rnd.huawei.com/artifactory/api/npm/npm-central-repo/,https://repo.harmonyos.com/ohpm/',
      // eslint-disable-next-line max-len
      'ohpm config set @ohpm-test:registry https://cmc.centralrepo.rnd.huawei.com/artifactory/api/npm/product_npm/,https://repo.harmonyos.com/ohpm/',
      // eslint-disable-next-line max-len
      'ohpm config set @ohos:registry https://cmc.centralrepo.rnd.huawei.com/artifactory/api/npm/product_npm/,https://repo.harmonyos.com/ohpm/',
      'ohpm config set strict_ssl false',
      'ohpm config set log_level info',
      'ohpm config set no_proxy .huawei.com',
    ];

    for (const commandStr of configList) {
      execa.commandSync(commandStr);
    }
  }

  private async deleteOldSubdirectories(dir: string, days: number) {
    const timeLimit = Date.now() - days * 24 * 60 * 60 * 1000;

    const files = await fse.readdir(dir);

    for (const file of files) {
      const filePath = path.join(dir, file);
      const stats = await fse.stat(filePath);

      // 检查这是否是一个目录并且是否超过了时间限制
      if (stats.isDirectory() && stats.mtime.getTime() < timeLimit) {
        await fse.remove(filePath);
      }
    }
  }
}

export const taskCenter: TestTaskCenter = new TestTaskCenter();
